#include "stdafx.h"
#include <msclr\marshal_cppstd.h>
using namespace msclr::interop;

#include "NetworkFacade.h"
#include "ProactorFactory.h"
#include "MessageHeader.h"
#include "SimpleConfig.h"
using namespace HalfNetwork;

#include "HalfNetworkClient.h"
#include "ErrorCode.h"


namespace HalfNetworkNET {
	HNNClient::HNNClient()
	{
	}

	HNNClient::~HNNClient()
	{
	}

	void HNNClient::Init()
	{
		ACE::init();
	}

	bool HNNClient::Run()
	{
		Update();
		return true;
	}

	void HNNClient::End()
	{
		NetworkInstance->Close();
		NetworkInstance->Destroy();

		ACE::fini();
	}

	UINT16 HNNClient::ReadConfigFile( ClientConfig% config )
	{
		HalfNetwork::SimpleConfig configReader;
		if( false == configReader.ReadFile( CLIENT_CONFIG_FILE_NAME ) ) {
			return ERRORCODE::CLIENT_FAILED_CONFIGFILE;
		}
	
		config.WorkerThreadCount = configReader.GetValue<UCHAR>(ACE_TEXT("WorkerThread"));
		config.IntervalSendTerm = configReader.GetValue<UINT32>(ACE_TEXT("UpdateTerm"));
		config.ReceiveBufferLength = configReader.GetValue<UINT32>(ACE_TEXT("ReceiveBufferLength"));
		
		INT32 DirectSend = configReader.GetValue<INT32>(ACE_TEXT("IS_DIRECT_SEND"));
		if( 1 == DirectSend ) { 
			config.SendMode = HalfNetwork::eSM_Direct;
			config.IsDirectSend = true;
		} else { 
			config.SendMode = HalfNetwork::eSM_Interval;
			config.IsDirectSend = false;
		}

		config.PrepareMemoryBlockSize = configReader.GetValue<UINT16>(ACE_TEXT("PrepareMemoryBlockSize"));
		config.PrepareMemoryBlockCount = configReader.GetValue<UINT32>(ACE_TEXT("PrepareMemoryBlockCount"));

		return ERRORCODE::NONE;
	}
	
	UINT16 HNNClient::CreateHalfNetworkInstance( ClientConfig config )
	{
		if( false == NetworkInstance->Create<HalfNetwork::ProactorFactory>() ) {
			return ERRORCODE::CREATE_PROACTORFACTORY;
		}
	
		if( config.PrepareMemoryBlockSize > 0 && config.PrepareMemoryBlockCount > 0 )
		{
			NetworkInstance->PrepareMemoryBlock( config.PrepareMemoryBlockSize, config.PrepareMemoryBlockCount );
		}
				
		return ERRORCODE::NONE;
	}
	
	UINT16 HNNClient::Open()
	{
		UINT16 nRet = ERRORCODE::NONE;
		
		nRet = ReadConfigFile( m_Config );
		if( ERRORCODE::NONE != nRet ) { 
			return nRet;
		}

		nRet = CreateHalfNetworkInstance( m_Config );
		if( ERRORCODE::NONE != nRet ) { 
			return nRet;
		}

		nRet = AddTimers();
		if( ERRORCODE::NONE != nRet ) { 
			return nRet;
		}


		HalfNetwork::SystemConfig HFConfig;
		HFConfig.Worker_Thread_Count = m_Config.WorkerThreadCount;
		HFConfig.Interval_Send_Term = m_Config.IntervalSendTerm;
		HFConfig.Receive_Buffer_Len = m_Config.ReceiveBufferLength;
		HFConfig.Send_Mode = (HalfNetwork::ESendMode)m_Config.SendMode;
		
		if( false == NetworkInstance->Open( &HFConfig ) ) {
			return ERRORCODE::CLIENT_OPEN;
		}

		return ERRORCODE::NONE;
	}

	bool HNNClient::Connect( String^ IP, const UINT16 Port, const UCHAR QueueID )
	{
		bool bRet = false;
		const wchar_t* szIP;

		{
			marshal_context ctx;
			szIP = ctx.marshal_as<const wchar_t*>( IP );
			bRet = NetworkInstance->Connect( szIP, Port, QueueID );
		}
		
		return bRet;
	}

	void HNNClient::Disconnect( const DWORD StreamID )
	{
		NetworkInstance->CloseStream( StreamID );
	}

	bool HNNClient::Send( const DWORD StreamID, const int Length, array<Byte>^ SendData )
	{
		if( Length < 1 || SendData->Length < 1 || Length > SendData->Length ) { 
			return false;
		}

		pin_ptr<Byte> pData = &SendData[0];
		
		ACE_Message_Block* block = NetworkInstance->AllocateBlock( Length );
		block->copy( (const char*)pData, Length );
		NetworkInstance->SendRequest( StreamID, block );

		pData = nullptr;
		return true;
	}

	bool HNNClient::InternalSend( const int Length, array<Byte>^ SendData )
	{
		if( Length < 1 || SendData->Length < 1 || Length > SendData->Length ) { 
			return false;
		}
		
		pin_ptr<Byte> pData = &SendData[0];

		ACE_Message_Block* block = NetworkInstance->AllocateBlock( Length );
		block->copy( (const char*)pData, Length );
		NetworkInstance->PushCustomMessage( CLIENT_INTERNAL_QUEUE_ID, block );

		pData = nullptr;
		return true;
	}

	void HNNClient::Update()
	{
		ProcessRecvQueue();
	}

	void HNNClient::ProcessRecvQueue()
	{
		ACE_Message_Block* headBlock = NULL;
		ACE_Message_Block* commandBlock = NULL;
    
		bool popResult = NetworkInstance->PopAllMessage( &headBlock, -1 );
		if (false == popResult) {
			return;
		}

		commandBlock = headBlock;
		while(NULL != commandBlock)
		{
			MessagePostee postee;
			memcpy(&postee, commandBlock->rd_ptr(), sizeof(MessagePostee));
			commandBlock->rd_ptr(sizeof(MessagePostee));
			ACE_Message_Block* payloadBlock = commandBlock->cont();

			array< Byte >^ byteArray = gcnew array< Byte >(payloadBlock->length());
			if( 0 < payloadBlock->length() ) { 
				System::Runtime::InteropServices::Marshal::Copy((IntPtr)payloadBlock->rd_ptr(),byteArray, 0, payloadBlock->length() );
			}
			
			switch(postee.command)
			{
			case eMH_Establish:
				{
					String^ IP = System::Text::Encoding::ASCII->GetString(byteArray);
					OnConnect( postee.stream_id, IP );
				}
				break;
			case eMH_Read:
				OnRead( postee.stream_id, byteArray );
				break;
			case eMH_Close:
				OnDisConnect( postee.stream_id );
				break;
			case eMH_Custom:
				OnRead( postee.stream_id, byteArray );
				break;
			case eMH_Timer:
				OnTimer( postee.stream_id );
				break;
			}
			commandBlock = payloadBlock->cont();
		}
		headBlock->release();
	}

	UINT16 HNNClient::AddTimer( const UINT32 timerID, const UINT32 interval, const UINT32 start )
	{
		if( false == NetworkInstance->AddTimer( timerID, interval, start ) ) { 
			return ERRORCODE::FAILED_ADD_TIMER;
		}	
		return ERRORCODE::NONE;
	}
}